package com.syf.starter.core.netty.handle;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.syf.core.service.SyServiceDefinition;
import com.syf.core.service.context.ServiceRequest;
import com.syf.core.service.context.ServiceResponse;
import com.syf.core.service.invoke.ServiceInvokeCallback;
import com.syf.core.service.invoke.SyServiceInvoker;
import com.syf.starter.config.SyHttpServerConfiguration;
import com.syf.starter.core.netty.tool.MsgCovert;
import com.syf.starter.core.netty.tool.spi.SyfServerInvokerShape;
import com.syf.starter.core.netty.tool.SyfServiceRoute;
import com.syf.starter.core.netty.tool.spi.impl.FairTokenBucketShape;
import com.syf.starter.core.netty.tool.impl.HttpMsgConvert;
import com.syf.starter.core.netty.tool.impl.HttpUriRoute;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@ChannelHandler.Sharable
public class HttpServerHandle extends SimpleChannelInboundHandler<FullHttpRequest> {

    private final SyHttpServerConfiguration configuration;

    private SyfServerInvokerShape shape;

    private final SyServiceInvoker serviceInvoker;

    private SyfServiceRoute<FullHttpRequest> serviceRoute;

    private MsgCovert<FullHttpRequest> msgCovert;


    public HttpServerHandle(SyHttpServerConfiguration configuration, SyServiceInvoker serviceInvoker) {
        this.configuration = configuration;
        this.serviceInvoker = serviceInvoker;

        initializer();
    }

    void initializer() {
        // 根据SPI设置限流器
        List<String> factoryNames = SpringFactoriesLoader.loadFactoryNames(SyfServerInvokerShape.class, this.getClass().getClassLoader());
        if (factoryNames.size() < 1) {
            this.shape = new FairTokenBucketShape(serviceInvoker.getActuatorMaxThreadNum(), configuration.getAvgResponseSeconds());
        } else {
            try {
                Class<?> shapeClass = Class.forName(factoryNames.get(0));
                this.shape = (SyfServerInvokerShape) shapeClass.getConstructor(Integer.class, Float.class)
                        .newInstance(serviceInvoker.getActuatorMaxThreadNum(), configuration.getAvgResponseSeconds());
            } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
                     InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        // 初始service路由器
        this.serviceRoute = new HttpUriRoute(configuration.getWebContext());

        // 初始化Msg转换器
        ObjectMapper jsonMapper = new ObjectMapper();
        jsonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //在序列化时忽略值为 null 的属性
        jsonMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
        // 在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ ,比如如果一个类中有private Date date;这种日期属性，序列化后为：{"date" : 1413800730456}，若不为true，则为{"date" : "2014-10-20T10:26:06.604+0000"}
        jsonMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 允许序列化空的POJO类
        jsonMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        // 把java.util.Date, Calendar输出为数字(时间戳)
        jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 在遇到未知属性的时候不抛出异常
        jsonMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        this.msgCovert = HttpMsgConvert.getINSTANCE(jsonMapper, new XmlMapper());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
        // 1. 流量控制
        if (!shape.tryAcquire(1, TimeUnit.SECONDS)) {
            log.error("service actuator is too busy!");
            ServiceResponse response = new ServiceResponse("90101", "service actuator is too busy!");
            processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
            return;
        }
        // 2. service 路由
        SyServiceDefinition serviceDefinition = serviceRoute.route(fullHttpRequest, serviceInvoker);
        if (serviceDefinition == null) {
            ServiceResponse response = new ServiceResponse("90103", "未找到对应的交易!");
            processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
            return;
        }
        // 3. 按mediaType完成报文转换
        ServiceRequest serviceRequest = msgCovert.convertRequest(serviceDefinition, fullHttpRequest);
        if (serviceRequest == null) {
            ServiceResponse response = new ServiceResponse("90104", "非法请求报文!");
            processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
            return;
        }

        // 4.调研service
        serviceInvoker.asyncInvokeService(serviceDefinition, serviceRequest, new ServiceInvokeCallback<ServiceResponse>() {

            @Override
            public void onSuccess(ServiceResponse response) {
                try {
                    processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void onFailure(ServiceResponse response) {
                try {
                    processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            @Override
            public void throwable(Exception e) {
                log.error("执行交易：[" + serviceDefinition.getServiceCode() + "]出现异常", e);
                ServiceResponse response = new ServiceResponse("90102", "执行交易出现异常!");
                try {
                    processResponse(ctx, msgCovert.convertResponse(fullHttpRequest, response));
                } catch (Exception ex) {
                    throw new RuntimeException(ex);
                }
            }
        });
    }

    public void processResponse(ChannelHandlerContext ctx, ByteBuf respBuf) {
        DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, respBuf);
        ctx.writeAndFlush(httpResponse);
        ctx.channel().close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("server端，出现异常！", cause);
        DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR
                , Unpooled.wrappedBuffer(("服务端出现异常!" + cause.getMessage()).getBytes(StandardCharsets.UTF_8)));
        ctx.writeAndFlush(httpResponse);
        ctx.channel().close();
    }
}
